#!/usr/env/bin python

"""simplegui - a simple gui interfaces for open.

Functions here are:

choose - lets the user choose an application from a list of applications.
find - lets the user find an application in the file system.
"""

import os, stat, fnmatch
from open.main import Application, OpenError
import open.help as help
from Tkinter import Button, Checkbutton, Entry, Frame, IntVar, StringVar,\
     Label, Listbox, END, SINGLE, SUNKEN, N, S, E, W
from FileDialog import LoadFileDialog
from tkMessageBox import showinfo


def choose(filename, apps):
    """Choose from a list of applications that know how to open a file."""

    app = AppChooser(filename, apps)
    app.master.title("Application to  open " + filename)
    app.mainloop()
    if not app.application:
        raise OpenError, "User canceled open of %s." % filename
    return app.application

def find(filename, have):
    """Let the user choose an application to open filename.

    Offer them the option to save this application in .openrc."""

    app = AppSaver(filename)
    app.master.title("Open " + filename)
    app.hide()
    
    fn = AppDialog(app, "Application to open " + filename)
    path = fn.go('/usr/X11R6/bin')

    if not path:
        raise OpenError, "User did not select a file."

    app.set_command(path)
    app.set_flags(have)
    app.show()
    app.mainloop()
    if not app.application:
        raise OpenError, "User canceled open of %s." % filename

    return app.application


class AppChooser(Frame):
    """Let the user select one from a list of applications."""

    def __init__(self, filename, applist,  master = None):
        self.filename = filename
        self.applist = applist
        self.application = None

        Frame.__init__(self, master)
        self.grid(sticky = E + W)

        top = self.winfo_toplevel()
        top.columnconfigure(0, weight = 1)
        self.columnconfigure(1, weight = 1)
        top.rowconfigure(0, weight = 1)
        self.rowconfigure(0, weight = 1)

        self.apps = Listbox(self, selectmode = SINGLE)
        self.apps.grid(sticky = N + S + E + W, columnspan = 3)
        for app in self.applist:
            self.apps.insert(END, app.name)
        self.apps.bind("Double-Button-1>", self.done)
        self.apps.none_sel = object()
        self.apps.current = self.apps.none_sel

        Label(self, text = "Command:").grid(sticky = E)
        self.command = Label(self, relief = SUNKEN,anchor = W)
        self.command.grid(row = 1, column = 1, columnspan = 2, sticky = E + W)

        Label(self, text = "Extensions:").grid(sticky = E)
        self.extensions = Label(self, relief = SUNKEN, anchor = W)
        self.extensions.grid(row = 2, column = 1, columnspan = 2,
                             sticky = E + W)

        Label(self, text = "Flags:").grid(sticky = E)
        self.flags = Label(self, relief = SUNKEN, anchor = W)
        self.flags.grid(row = 3, column = 1, columnspan = 2, sticky = E + W)

        Button(self, text = "Open",  command = self.done).grid()
        Button(self, text = "Browse", command = self.find).grid(row = 4,
                                                                column = 1)
        Button(self, text = "Cancel", command = self.quit).grid(row = 4,
                                                                column = 2)

        self.poll()	# Kick off the timer on the Listbox. Bleah.

    def poll(self):
        """Sigh. Check the ListBox to see if it's changed. Effing Tkinter."""

        sel = self.apps.curselection()
        if sel:
            now = int(sel[0])
        else:
            now = self.apps.none_sel
        if now != self.apps.current:
            self.apps.current = now
            self.apps_changed()
        self.after(250, self.poll)

    def apps_changed(self):
        """The listbox changed - update the label widgets with the new app."""

        if self.apps.current == self.apps.none_sel:
            self.command['text'] = ""
            self.extensions['text'] = ""
            self.flags['text'] = ""
        else:
            app = self.applist[self.apps.current]
            command = app.command(self.filename)
            if not hasattr(command, 'join'):
                command = ' '.join(command)
            self.command['text'] = command
            self.extensions['text'] = ' '.join(app.extensions)
            self.flags['text'] = ' '.join(app.flags)

    def find(self):
        """Let the user browse the filesystem for an application."""

        self.winfo_toplevel().deiconify()	# Hide myself
        self.application = find(self.filename, self.applist.have)
        self.quit()

    def done(self):
        """Open the application."""

        self.application = self.applist[self.apps.current]
        self.quit()


class AppSaver(Frame):
    """Get an application name, then let the user use/save it."""

    def __init__(self, filename, master = None):
        self.filename = filename
        self.application = None

        Frame.__init__(self, master)
        self.grid(sticky = E + W)

        top = self.winfo_toplevel()
        top.columnconfigure(0, weight = 1)
        self.columnconfigure(1, weight = 1)

        Label(self, text = "Command:").grid(sticky = E)
        self.command = StringVar()
        Entry(self, textvariable = self.command).grid(row = 0, column = 1,
                                                      sticky = E + W)
        Button(self, text = "?", command = self.command_help).grid(row = 0,
                                                                   column = 2)

        Label(self, text = "Name:").grid(sticky = E)
        self.name = StringVar()
        Entry(self, textvariable = self.name).grid(row = 1, column = 1,
                                                   sticky = E + W)
        Button(self, text = "?", command = self.name_help).grid(row = 1,
                                                                column = 2)

        Label(self, text = "Extensions:").grid(sticky = E)
        self.extensions = StringVar()
        _, ext = os.path.splitext(filename)
        self.extensions.set(ext[1:])
        Entry(self, textvariable = self.extensions).grid(row = 2, column = 1,
                                                         sticky = E + W)
        Button(self, text = "?", command = self.extensions_help).grid(row = 2,
                                                                      column = 2)

        Label(self, text = "Flags:").grid(sticky = E)
        self.flags = StringVar()
        Entry(self, textvariable = self.flags).grid(row = 3, column = 1,
                                                    sticky = E + W)
        Button(self, text = "?", command = self.flags_help).grid(row = 3,
                                                                 column = 2)

        self.save = IntVar()
        Checkbutton(self, text = "Save this application?",
                    variable = self.save).grid(column = 1)
        self.save.set(1)
        
        Button(self, text = "Open", command = self.done).grid()
        Button(self, text = "Cancel", command = self.quit).grid(row = 5,
                                                                sticky = E,
                                                                column = 1,
                                                                columnspan = 2)

    def done(self):
        """Save the Application in self, and possibly to ~/.openrc."""

        app = Application(flags = self.flags.get(),
                          extensions = self.extensions.get(),
                          name = self.name.get(),
                          command = self.command.get())
        if self.save.get():
            app.save()
        self.application = app
        self.quit()

    def set_command(self, path):
        """Set the command to run."""

        self.command.set(path)
        self.name.set(os.path.basename(path))

    def set_flags(self, flags):
        self.flags.set(' '.join(flags))

    def show(self):
        self.winfo_toplevel().deiconify()

    def hide(self):
        self.winfo_toplevel().withdraw()

    def command_help(self):
        showinfo(title = "Command help", message = help.command)

    def name_help(self):
        showinfo(title = "Name help", message = help.name)

    def flags_help(self):
        showinfo(title = "Flags help", message = help.flags)

    def extensions_help(self):
        showinfo(title = "Extensions help", message = help.extensions)


class AppDialog(LoadFileDialog):
    """Choose an executable file to load."""

    def filter_command(self):
        # This is mostly cut-n-paste from FileDialog.LoadFileDialog.
        # But this doesn't call a method to do the actual filtering.
        dir, pat = self.get_filter()
        try:
            names = os.listdir(dir)
        except os.error:
            self.master.bell()
            return
        self.directory = dir
        self.set_filter(dir, pat)
        names.sort()
        subdirs = [os.pardir]
        matchingfiles = []
        for name in names:
            fullname = os.path.join(dir, name)
            if os.path.isdir(fullname):
                subdirs.append(name)
            elif fnmatch.fnmatch(name, pat):
                try:
                    st = os.stat(fullname)
                except os.error:
                    continue
                if stat.S_IEXEC & st.st_mode:
                    matchingfiles.append(name)
        self.dirs.delete(0, END)
        for name in subdirs:
            self.dirs.insert(END, name)
        self.files.delete(0, END)
        for name in matchingfiles:
            self.files.insert(END, name)
        head, tail = os.path.split(self.get_selection())
        if tail == os.curdir: tail = ''
        self.set_selection(tail)
    
